package handling.channel.handler;

import client.MapleCharacter;
import client.MapleClient;
import client.inventory.MapleInventoryType;
import constants.GameConstants;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import scripting.map.NodeScriptManager;
import server.MapleInventoryManipulator;
import server.Timer;
import server.life.MapleLifeFactory;
import server.life.MapleMonster;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.maps.MapleMap;
import server.maps.MapleMapObjectType;
import server.maps.MapleNodes;
import server.movement.LifeMovementFragment;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
import tools.Triple;
import tools.data.input.LittleEndianAccessor;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.packet.MobPacket;

import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MobHandler {

    private static final Logger log = LogManager.getLogger(MobHandler.class);
    /*
     * 怪物移动
     */

    public static void MoveMonster(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) {
        if (chr == null || chr.getMap() == null) {
            return;
        }
        int objectid = slea.readInt();
        MapleMonster monster = chr.getMap().getMonsterByOid(objectid);
        if (monster == null || monster.getType() != MapleMapObjectType.MONSTER) {
            return;
        }
        if (monster.getLinkCID() > 0) {
            return;
        }
        List<LifeMovementFragment> res;

//        if (chr.isShowPacket()) {
//            String dropString = slea.toString(true).substring(0, 60);
//            chr.dropDebugMessage(dropString);
//        }

//        slea.skip(1); //[00]
        short moveid = slea.readShort();
        boolean useSkill = (slea.readByte() & 0xFF) > 0;
        byte mode = slea.readByte();
        int skillId = slea.readByte() & 0xFF; //是否使用技能
        int skillLevel = slea.readByte(); //技能ID
        short effectAfter = slea.readShort(); //使用技能的延迟时间

        int realskill = 0;
        int level = 0;
        
        /*
         * 8850003 - 伊卡尔特
         * 以上怪物移动会出现 aa > 0
         */
        List<Pair<Short, Short>> list = new ArrayList<>();
        byte aa = slea.readByte();
        if (aa > 0) {
            for (int i = 0; i < aa; i++) {
                list.add(new Pair<>(slea.readShort(), slea.readShort()));
            }
        }
        /*
         * 8300007 - 御龙魔 - gg = 7
         * 8840000 - 班·雷昂 - gg = 5
         * 8860000 - 阿卡伊勒 - gg = 5
         * 以上怪物移动会出现 gg > 0
         */
        final HashMap<Integer, Short> hashMap = new HashMap<Integer, Short>();
        final byte a4;
        if ((a4 = slea.readByte()) > 0) {
            for (byte b4 = 0; b4 < a4; ++b4) {
                hashMap.put((int) b4, slea.readShort());
            }
        }
        final HashMap<Integer, Integer> hashMap2 = new HashMap<Integer, Integer>();
        final byte gg;
        if ((gg = slea.readByte()) > 0) {
            for (byte b5 = 0; b5 < gg; ++b5) {
                hashMap2.put((int) b5, slea.readInt());
            }
        }
        slea.skip(1); //[00]
        boolean skipped = slea.readInt() != 0 && gg > 0;
        slea.skip(8); //[CC DD FF 00 CC DD FF 00]
        slea.skip(5); //[FC 2A CC 63 01]
        slea.skip(4); //怪物的x y坐标
        short start_x = slea.readShort();
        short start_y = slea.readShort();
        Point startPos = new Point(start_x, start_y);
        slea.skip(4);
        if (monster.getId() == 9300281 && skipped) { //莱格斯
            if (slea.readByte() > 10) { //estimate
                slea.skip(8);
            } else {
                slea.seek(slea.getPosition() - 1);
            }
        }
        try {
            res = MovementParse.parseMovement(slea, 2);
        } catch (ArrayIndexOutOfBoundsException e) {
            MovementParse.log.error("怪物ID " + monster.getId() + ", AIOBE Type2:\r\n" + slea.toString(true), e);
            return;
        }
        monster.getMoveWLock().lock();
        try {
            if (res != null && res.size() > 0) {
                MapleMap map = chr.getMap();
                if (slea.available() != 29) {
                    MovementParse.log.error("slea.available != 29 (movement parsing error)\r\n怪物ID: " + monster.getId() + "\r\n" + slea.toString(true));
                    return;
                }

                if (useSkill || (monster.getStats().isSkeleton() && Randomizer.nextInt(10) < 7)) {
                    boolean used = false;
                    byte size = monster.getNoSkills();
                    if (size > 0) {
                        int random = Randomizer.nextInt(size);
                        Triple<Integer, Integer, Integer> skillToUse = monster.getSkills().get(random);
                        realskill = skillToUse.getLeft();
                        level = skillToUse.getMid();
                        MobSkill toUse = MobSkillFactory.getMobSkill(realskill, level);
                        if (toUse != null && !toUse.checkCurrentBuff(chr, monster)) {
                            final long currentTimeMillis = System.currentTimeMillis();
                            if (currentTimeMillis - monster.getLastSkillUsed(realskill) > toUse.getCoolTime() && !toUse.onlyOnce()) {
                                int reqHp = (int) ((float) monster.getHp() / (float) monster.getMobMaxHp() * 100.0F);
                                if (toUse.getMpCon() <= monster.getMp() && reqHp <= toUse.getHP()) {
                                    used = true;
                                    monster.setLastSkillUsed(realskill, currentTimeMillis, toUse.getCoolTime());
                                }
                            }
                        }
                    }
                    if (used && monster.getStats().isSkeleton()) {
                        skillId = realskill;
                        skillLevel = level;
                        realskill = 0;
                        level = 0;
                    }
                    if (!used) {
                        realskill = 0;
                        level = 0;
                    }

                    if (chr.isShowPacket()) {
                        if (skillLevel > 0) {
                            chr.dropDebugMessage(1, "[怪物技能] 怪物:" + MapleLifeFactory.getMonsterName(monster.getId()) + "(" + monster.getId() + ") 技能ID:" + skillLevel + " action:" + skillId + " useSkill:" + useSkill + " after:" + effectAfter);
                        }
                    }
                }
                MovementParse.updatePosition(res, monster, -1);
                Point endPos = monster.getTruePosition();
                map.broadcastMessage(c.getPlayer(), MobPacket.moveMonster(useSkill, mode, skillId, skillLevel, effectAfter, objectid, startPos, res, list, hashMap, hashMap2), endPos);
                map.moveMonster(monster, monster.getPosition());

                if (skillId > 0 && skillLevel > 0 && monster.getNoSkills() > 0) {
                    for (Triple<Integer, Integer, Integer> pair : monster.getSkills()) {
                        if (pair.getLeft() == skillId && pair.getMid() == skillLevel) {
                            MobSkill skillData = MobSkillFactory.getMobSkill(skillId, skillLevel);
                            if (skillData != null) {
                                skillData.applyEffect(c.getPlayer(), monster, pair.getRight(), true);
                                break;
                            }
                            break;
                        }
                    }
                }
                if (monster.checkAggro(mode == -1 && !useSkill && startPos.distanceSq(endPos) <= 2.0) || monster.checkTrans(chr) || monster.getController() != chr) {
                    return;
                }
                if (!c.getPlayer().isAlive() || (skillId == -1 && monster.isControllerKnowsAboutAggro() && !monster.isFirstAttack())) {
                    monster.setControllerHasAggro(false);
                    monster.setControllerKnowsAboutAggro(false);
                }
                c.announce(MobPacket.moveMonsterResponse(objectid, moveid, monster.getMp(), monster.isControllerHasAggro() | monster.isControllerKnowsAboutAggro(), realskill, level));
            }
        } finally {
            monster.getMoveWLock().unlock();
        }
    }

    public static void MobHitByObstacleAtom(LittleEndianAccessor slea, MapleCharacter chr) {
        int oid = slea.readInt();
        MapleMonster mob = chr.getMap().getMonsterByOid(oid);
        slea.skip(4);
        int n3 = slea.readInt();
        MapleMonster mob_ = chr.getMap().getMonsterByOid(n3);
        if (mob != null && mob_ != null && mob_.getStats().isFriendly()) {
            mob_.damage(chr, 100, true);
            checkKenta(chr, mob_, chr.getMap());
        }
    }

    public static void checkKenta(MapleCharacter player, MapleMonster monster, MapleMap map) {
        if (!monster.isAlive() && monster.getStats().isEscort()) {
            for (MapleCharacter chr : map.getCharactersThreadsafe()) {
                if (chr.getParty() == null || chr.getParty().getLeader().getId() != chr.getId()) continue;
                if (!chr.haveItem(2022698)) break;
                MapleInventoryManipulator.removeById(chr.getClient(), MapleInventoryType.USE, 2022698, 1, false, true);
                monster.heal((int) monster.getMobMaxHp(), monster.getMobMaxMp(), true);
                return;
            }
            map.broadcastMessage(MaplePacketCreator.serverWithoutPrefix("你的队伍任务失败了！"));
            MapleMap map1 = player.getMap().getForcedReturnMap();
            for (MapleCharacter c : map.getCharactersThreadsafe()) {
                c.changeMap(map1, map1.getPortal(0));
            }
        } else if (monster.getStats().isEscort() && monster.getEventInstance() != null) {
            switch (monster.getId()) {
                case 9300460: {
                    if (player.getMapId() == 923040300) {
                        player.getMap().startMapEffect("呃啊！洞窟蝙蝠！！！呃啊啊，请救救我！", 5120052);
                        break;
                    }
                    player.getMap().startMapEffect("呃啊啊~好可怕. 快走开! ! !", 5120052);
                }
            }
            monster.getEventInstance().setProperty("HP", String.valueOf(monster.getHp()));
        }
    }

    /*
     * 怪物攻击怪物
     * 月妙任务出现
     */
    public static void MobHitByMob(LittleEndianAccessor slea, MapleCharacter chr) {
        MapleMap map = chr.getMap();
        if (map == null) {
            return;
        }
        MapleMonster mobfrom = map.getMonsterByOid(slea.readInt());
        slea.skip(4); // 角色ID
        MapleMonster mobto = map.getMonsterByOid(slea.readInt());

        if (mobfrom != null && mobto != null && mobto.getStats().isFriendly()) {
            int damage = (mobto.getStats().getLevel() * Randomizer.nextInt(mobto.getStats().getLevel())) / 2; // Temp for now until I figure out something more effective
            mobto.damage(chr, damage, true);
            checkShammos(chr, mobto, map);
        }
    }

    public static void checkShammos(MapleCharacter chr, MapleMonster mobto, MapleMap map) {
        if (!mobto.isAlive() && mobto.getStats().isEscort()) { //shammos
            for (MapleCharacter chrz : map.getCharactersThreadsafe()) { //check for 2022698
                if (chrz.getParty() != null && chrz.getParty().getLeader().getId() == chrz.getId()) {
                    //leader
                    if (chrz.haveItem(2022698)) { //万年冰河水
                        MapleInventoryManipulator.removeById(chrz.getClient(), MapleInventoryType.USE, 2022698, 1, false, true);
                        mobto.heal((int) mobto.getMobMaxHp(), mobto.getMobMaxMp(), true);
                        return;
                    }
                    break;
                }
            }
            map.broadcastMessage(MaplePacketCreator.serverWithoutPrefix("Your party has failed to protect the monster."));
            MapleMap mapp = chr.getMap().getForcedReturnMap();
            for (MapleCharacter chrz : map.getCharactersThreadsafe()) {
                chrz.changeMap(mapp, mapp.getPortal(0));
            }
        } else if (mobto.getStats().isEscort() && mobto.getEventInstance() != null) {
            mobto.getEventInstance().setProperty("HP", String.valueOf(mobto.getHp()));
        }
    }

    /*
     * 怪物自爆
     * 8500003 - 小黑水雷
     * 8500003 - 小黑水雷
     */
    public static void MonsterBomb(int oid, MapleCharacter chr) {
        MapleMonster monster = chr.getMap().getMonsterByOid(oid);
        if (monster == null || !chr.isAlive() || chr.isHidden() || monster.getLinkCID() > 0) {
            return;
        }
        byte selfd = monster.getStats().getSelfD();
        if (selfd != -1) {
            chr.getMap().killMonster(monster);
        }
    }

    /*
     * 怪物仇恨
     */
    public static void AutoAggro(LittleEndianAccessor lea, MapleCharacter chr) {
//        log.trace("调用");
        if (chr == null || chr.getMap() == null || chr.isHidden()) { //no evidence :)
            return;
        }
        MapleMonster monster = chr.getMap().getMonsterByOid(lea.readInt());
        if (monster == null) {
            return;
        }
        int n3 = chr.isAlive() ? lea.readInt() : 0;
        double d2 = chr.getEventInstance() != null ? GameConstants.maxViewRangeSq_Half() : 100000.0;
        if (monster.isAlive() && chr.getTruePosition().distanceSq(monster.getTruePosition()) < d2) {
            monster.getAggroRank().put(chr.getName(), n3);
            if (monster.getController() == chr) {
                monster.setControllerKnowsAboutAggro(true);
            }
        } else {
            monster.getAggroRank().put(chr.getName(), 0);
            if (monster.getController() == chr && chr.getEventInstance() == null) {
                monster.setControllerKnowsAboutAggro(false);
            }
        }
    }

    public static void MonsterSpecialSkill(LittleEndianAccessor slea, MapleCharacter chr) {
        if (chr == null) {
            return;
        }

        int moboid = slea.readInt();
        int unk_1 = slea.readInt();
        int unk_2 = slea.readInt();
        int unk_3 = slea.readInt();
        int unk_4 = slea.readInt();

//        chr.send(MobPacket.showMonsterSpecialSkill(moboid));
    }

    public static void MobTimeBombEnd(LittleEndianAccessor lea, MapleCharacter player) {
        MapleMonster monster = player.getMap().getMonsterByOid(lea.readInt());
        if (monster != null) {
            player.getClient().announce(MobPacket.mobEscortReturnBefore(monster, player.getMap()));
        }
    }

    public static void MobEscortCollision(LittleEndianAccessor lea, MapleCharacter player) {
        MapleMonster monster = player.getMap().getMonsterByOid(lea.readInt());
        int n2 = lea.readInt();
        int n3 = player.getMap().getNodes().size();
        if (monster != null && n3 > 0) {
            MapleNodes.MapleNodeInfo node = player.getMap().getNode(n2);
            if (node == null) {
                return;
            }
            if (node.attr == 2) {
                switch (player.getMapId() / 100) {
                    case 9211200:
                    case 9211201:
                    case 9211202:
                    case 9211203:
                    case 9211204: {
                        if (node.stopInfos == null || node.stopInfos.sayInfo.size() <= 0) break;
                        player.getMap().talkMonster(node.stopInfos, 5120035, monster.getObjectId());
                        break;
                    }
                    case 9320001:
                    case 9320002:
                    case 9320003: {
                        if (node.stopInfos == null || node.stopInfos.sayInfo.size() <= 0) break;
                        player.getMap().talkMonster(node.stopInfos, 5120051, monster.getObjectId());
                        break;
                    }
                    case 9230402: {
                        if (node.stopInfos == null || node.stopInfos.sayInfo.size() <= 0) break;
                        player.getMap().talkMonster(node.stopInfos, 5120052, monster.getObjectId());
                    }
                }
                if (!node.stopInfos.scriptName.isEmpty()) {
                    NodeScriptManager.getInstance().getNodeScript(player.getClient(), node.stopInfos.scriptName, node.stopInfos.stopDuration, monster.getObjectId());
                }
                if (node.stopInfos.sayInfo.size() > 0) {
                    Timer.MapTimer.getInstance().schedule(() -> {
                                player.send(MobPacket.MobEscortStopEndPermmision(monster.getObjectId()));
                            }
                            , node.stopInfos.stopDuration);
                }
            }
            monster.setLastNode(n2);
            if (player.getMap().isLastNode(n2)) {
                switch (player.getMapId() / 100) {
                    case 9211200:
                    case 9211201:
                    case 9211202:
                    case 9211203:
                    case 9211204:
                    case 9320001:
                    case 9320002:
                    case 9320003: {
                        player.send_other(MaplePacketCreator.showEffect("aswan/clear"), true);
                        player.send_other(MaplePacketCreator.playSound("Party1/Clear"), true);
                        player.getMap().broadcastMessage(MaplePacketCreator.serverEvent("进入下一个阶段。"));
                        player.getEventInstance().setProperty("checkdone", String.valueOf(player.getMapId()));
                        break;
                    }
                    case 9230401:
                    case 9230402: {
                        player.send_other(MaplePacketCreator.showEffect("aswan/clear"), true);
                        player.send_other(MaplePacketCreator.playSound("Party1/Clear"), true);
                        player.getEventInstance().getEventManager().setProperty("state", "" + (player.getMapId() / 100 % 10 + 1) + "");
                    }
                }
            }
        }
    }
}
